package com.puzzlebazar.client.puzzleinfo;

import java.util.LinkedList;
import java.util.List;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.puzzlebazar.client.services.PuzzleServiceAsync;
import com.puzzlebazar.client.services.ServiceFactory;
import com.puzzlebazar.client.user.UserModel;
import com.puzzlebazar.client.util.Observable;
import com.puzzlebazar.client.util.ObservableImpl;
import com.puzzlebazar.client.util.Observer;

/**
 * @author beaudoin
 *
 */
public class HistoryList extends ObservableImpl implements Observer {

	private final UserModel userModel;
	private final PuzzleInfoModel model;

	private List<HistoryInfo> historyList = null;
	private boolean refreshing = false;
	private boolean autoRefresh = false;
	

	/**
	 * Creates an history list
	 * 
	 * @param userModel The user model for which to build an history list, null to build a public list
	 * @param model The puzzle info model for which to retrieve histories
	 */
	public HistoryList( UserModel userModel, PuzzleInfoModel model ) {
		this.userModel = userModel;
		if( userModel != null )
			userModel.addObserver( this );
		this.model = model;
		model.addObserver( this );
	}
	
	/**
	 * @return True if the history list is valid, false otherwise
	 */
	public boolean isValid() {
		return historyList != null;
	}
	
	/**
	 * @return True if the history list is waiting to be refreshed by an asynchronous call
	 */
	public boolean isRefreshing() {
		return refreshing;
	}

	/**
	 * @return The number of puzzles in this list
	 */
	public int getHistoryCount() {
		if( historyList == null || isRefreshing() ) return 0;
		return historyList.size();
	}
	

	/**
	 * Access the information for the history at the given index  
	 * 
	 * @param index The index at which to access the history information
	 * @return The information for the accessed history
	 * @throws ArrayIndexOutOfBoundsException 
	 */
	public HistoryInfo getHistoryInfo( int index ) throws ArrayIndexOutOfBoundsException  {
		if( refreshing ) throw new ArrayIndexOutOfBoundsException();
		return historyList.get(index);
	}
	

	/**
	 * Access the information for the puzzle attached to this list
	 * 
	 * @return The information for the puzzle
	 */
	public PuzzleInfo getPuzzleInfo() {
		return model.getPuzzleInfo();
	}
	
	/**
	 * Refresh the history list from the server using the current author email and puzzleInfoKey
	 * Call notifyObservers, refreshing state may have changed
	 */
	public void refresh() {

		
		if( !refreshing ) {
			refreshing = true;
			setChanged();
		}

		String authorEmail = null;
		if( userModel != null ) { 
			if( userModel.getUserInfo() != null )
				authorEmail = userModel.getUserInfo().getEmail();
			else {
				historyList = new LinkedList<HistoryInfo>();
				refreshing = false;
				setChanged();
				return;
			}
		}
		
		String puzzleInfoKey = model.getPuzzleInfoKey();
		if( puzzleInfoKey == null )
			return;		
		
		PuzzleServiceAsync puzzleService = ServiceFactory.getPuzzleService();
		puzzleService.getHistoryList(authorEmail, puzzleInfoKey, new AsyncCallback< LinkedList<HistoryInfo> >() {

			@Override
			public void onSuccess(LinkedList<HistoryInfo> result) {
				refreshing = false;
				historyList = result;
				setChanged();
				notifyObservers();
			}

			@Override
			public void onFailure(Throwable caught) {
				refreshing = false;
				historyList = null;
				setChanged();
				notifyObservers();
			}
		} );
	
	}

	@Override
	public void update(Observable observable, Object arg) {
		if( autoRefresh )
			refresh();
		notifyObservers();
	}
	
	@Override
	public void release() {
		super.release();
		if( userModel != null )
			userModel.deleteObserver( this );
		model.deleteObserver(this);
	}

	/**
	 * Auto-refresh whenever the user or the puzzle info changes. Will
	 * not refresh right-away, so call refresh() if that's desired.
	 * 
	 * @param autoRefresh True if the list should auto-refresh when 
	 */
	public void setAutoRefresh(boolean autoRefresh) {
		this.autoRefresh  = autoRefresh;
	}
	
}
